using System;
using System.Collections;
using System.Runtime.Serialization;
using Evaluant.Uss;	
using Evaluant.Uss.Collections;
using System.Diagnostics;	

namespace Evaluant.Uss.ObjectContext
{

	[Serializable()]
    [DebuggerDisplay("Count = {Count}")]
    [DebuggerTypeProxy(typeof(CollectionDebugView))]
	public class  IPersistableCollection : IList, ISerializable 
	{
		private IList _List;

		[NonSerialized]
		protected ObjectContext _PersistenceManager;

		[NonSerialized]
		protected IPersistable _Parent;

		[NonSerialized]
		protected string _Role;

		public IPersistableCollection()
		{
			_List = new HashedList();
		}
		
		public IPersistableCollection(ObjectContext persistenceManager, IPersistable parent, string role, IEnumerable data)
		{
			_PersistenceManager = persistenceManager;
			_Parent = parent;
			_Role = role ;

			_List = new HashedList();
			if(data != null)
			{
//				_List = new HashedList();
				foreach(IPersistable o in data)
					_List.Add(o);
			}
			else
			{
				// create by dynamicProxy, fill the collection if already loaded
				if(parent.Entity.InferredReferences.Contains(role) && _PersistenceManager != null )
				{
					EntitySet entities = parent.Entity.GetEntitySet(role);
#if EUSS11
                    IList items = _PersistenceManager.LoadWithEntities(entities);
#else
					IList items = _PersistenceManager.LoadWithEntities<IPersistable>(entities);
#endif
					foreach(IPersistable o in items)
						_List.Add(o);
				}
			}
		}
		
		public IPersistableCollection(ICollection value)
		{
			this.AddRange(value);
		}
		
		public bool IsAttached
		{
			get { return _Parent != null && _Parent.Entity != null; }
		}

		public object this[int index] 
		{
			get 
			{
				EnsureLoaded();
				return ((IPersistable)(_List[index]));
			}
			set 
			{
				EnsureLoaded();
				if(IsAttached)
				{
					_Parent.Entity.DeleteElement(_Role, ((IPersistable)_List[index]).Entity);
					_Parent.Entity.AddValue(_Role, GetEntity(value));
				}

				_List[index] = value;
			}
		}

		private Entity GetEntity(object item)
		{
			IPersistable persistable = item as IPersistable;
					
			if(persistable != null)
				return persistable.Entity;
			else
				return _PersistenceManager.ExtractEntity(item);
		}

		public int Add(object item)
		{
			EnsureLoaded();
			// if the collection is not autonomous, add the added entity to the parent's role
			if(IsAttached)
			{
				_Parent.Entity.AddValue(_Role, GetEntity(item));

				// mark reference as "loaded"
				if(!_Parent.Entity.InferredReferences.Contains(_Role))
					_Parent.Entity.InferredReferences.Add(_Role);
			}

			return _List.Add(item);
		}

		public void AddRange(IEnumerable value)
		{
			EnsureLoaded();
			foreach(object item in value)
			{
				this.Add(item);
			}
		}
		
		public bool Contains(object value)
		{
			EnsureLoaded();
			return _List.Contains(value);
		}
		
		public void CopyTo(System.Array array, int index)
		{
			EnsureLoaded();
			_List.CopyTo(array, index);
		}
		
		public int IndexOf(object value)
		{
			EnsureLoaded();
			return _List.IndexOf(value);
		}
		
		public void Clear()
		{
			EnsureLoaded();

			if(IsAttached)
				_Parent.Entity.DeleteElement(_Role);

			_List.Clear();
		}

		public void Insert(int index,  object value)
		{
			EnsureLoaded();
			_List.Insert(index, value);
		}
		
		public int Count
		{
			get 
			{ 
				EnsureLoaded();
				return _List.Count; 
			}
		}

		public void RemoveAt(int index)
		{
			EnsureLoaded();
			_List.RemoveAt(index);
		}

		public IEnumerator GetEnumerator()
		{
			EnsureLoaded();
			return _List.GetEnumerator();
		}
		
		public void Remove(object item)
		{
			EnsureLoaded();
			if(IsAttached)
			{
				_Parent.Entity.DeleteElement(_Role, GetEntity(item));

				// mark reference as "loaded"
				if(!_Parent.Entity.InferredReferences.Contains(_Role))
					_Parent.Entity.InferredReferences.Add(_Role);
			}

			_List.Remove(item);
		}

		public bool IsSynchronized
		{
			get 
			{
				EnsureLoaded();
				return _List.IsSynchronized; 
			}
		}

		public object SyncRoot
		{
			get 
			{ 
				EnsureLoaded();
				return _List.SyncRoot; 
			}
		}

		public bool IsFixedSize
		{
			get 
			{ 
				EnsureLoaded();
				return _List.IsFixedSize; 
			}
		}

		public bool IsReadOnly
		{
			get 
			{	
				EnsureLoaded();
				return _List.IsReadOnly; 
			}
		}

		internal bool IsLoaded()
		{
			if(IsAttached)
				return _Parent.Entity.InferredReferences.Contains(_Role);

			return (_List != null);
		}

		internal void EnsureLoaded()
		{
			if(IsLoaded() || !ObjectContext.EnableLazyLoading)
				return;

			if(_PersistenceManager != null && IsAttached) 
			{
				_List = _PersistenceManager.LoadReference(_Parent.Entity, _Role);
			}
			else // Standalone collection
			{
				_List = new HashedList();
			}
		}

		public  ObjectContext ObjectContext
		{
			get { return _PersistenceManager; }
			set { _PersistenceManager = value; }
		}

		public string Role
		{
			get { return _Role; }
			set { _Role = value; }
		}

		#region ISerialiazable implementation

		void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context) 
		{
			info.SetType(typeof(ArrayList));

			ArrayList list = null;
			list = _List == null ? new ArrayList() : new ArrayList(_List);

			System.Reflection.MemberInfo[] memberInfos = FormatterServices.GetSerializableMembers(typeof(ArrayList));
			object[] memberValues = FormatterServices.GetObjectData(list, memberInfos);

			for(int i=0; i<memberInfos.Length; i++)
				info.AddValue(memberInfos[i].Name, memberValues[i]);
		}

		#endregion
	}
}
